package ddz.utils;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.chrono.IsoChronology;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * JDK8 日期时间（JSR 310规范）操作工具集
 *
 * @author zhoukai
 */
public class DateTimeUtils {

    public final static ZoneId CH_ZONE = ZoneId.of("Asia/Shanghai");

    /**
     * 格式化本地日期
     *
     * @param ld     本地日期
     * @param format yyyy-MM-dd
     * @return 格式化后的字符串
     */
    public final static String format(LocalDate ld, String format) {
        return format(ld, DateTimeFormatter.ofPattern(format));
    }

    public final static String format(LocalDate ld, DateTimeFormatter format) {
        return ld.format(format);
    }

    /**
     * 格式化本地日期时间
     *
     * @param ldt    本地日期时间
     * @param format yyyy-MM-dd HH:mm:ss
     * @return 格式化后的字符串
     */
    public final static String format(LocalDateTime ldt, String format) {
        return format(ldt, DateTimeFormatter.ofPattern(format));
    }

    public final static String format(LocalDateTime ldt, DateTimeFormatter format) {
        return ldt.format(format);
    }

    /**
     * 将日期时间转换为字符串表示形式
     *
     * @param date   日期对象
     * @param format 字符串显示的格式 yyyy-MM-dd HH:mm:ss
     * @return 日期时间的字符串表示
     */
    public final static String format(Date date, String format) {
        return format(date, new SimpleDateFormat(format));
    }

    public final static String format(Date date, SimpleDateFormat format) {
        return format.format(date);
    }

    public final static LocalDate parseToLocalDate(String dateTimeString, String format) {
        return parseToLocalDate(dateTimeString, DateTimeFormatter.ofPattern(format));
    }

    public final static LocalDate parseToLocalDate(String dateTimeString, DateTimeFormatter format) {
        return LocalDate.parse(dateTimeString, format);
    }

    public final static LocalDateTime parseToLocalDateTime(String dateTimeString, String format) {
        return parseToLocalDateTime(dateTimeString, DateTimeFormatter.ofPattern(format));
    }

    public final static LocalDateTime parseToLocalDateTime(String dateTimeString, DateTimeFormatter format) {
        return LocalDateTime.parse(dateTimeString, format);
    }

    public final static Date parseToDate(String dateTimeString, String format) {
        return parseToDate(dateTimeString, new SimpleDateFormat(format));
    }

    public final static Date parseToDate(String dateTimeString, DateFormat format) {
        try {
            return format.parse(dateTimeString);
        } catch (ParseException ex) {
            Logger.getLogger(DateTimeUtils.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }

    /**
     * 时间间隔类型枚举
     */
    static public enum Interval {

        /**
         * 年
         */
        YEAR,
        /**
         * 月
         */
        MONTH,
        /**
         * 日，起始时间点和结束时间点之间只要跨过0点即为一天
         */
        DAY,
        /**
         * 精确时间点的日
         */
        TIME_DAY,
        /**
         * 周
         */
        WEEK,
        /**
         * 精确到毫秒的周
         */
        TIME_WEEK,
        /**
         * 时
         */
        HOUR,
        /**
         * 分
         */
        MINUTE,
        /**
         * 秒
         */
        SECOND,
        /**
         * 毫秒
         */
        MILLIS
    }

    /**
     * 将从1970年开始的格林毫秒数据转换为本地时间点对象
     *
     * @param timeMills 从1970年开始的格林毫秒
     * @return 时间点对象
     */
    public final static Instant toInstant(long timeMills) {
        return Instant.ofEpochMilli(timeMills);
    }

    /**
     * 转换为本地日期时间对象
     *
     * @param instant 时间点对象
     * @return 本地日期时间
     */
    public final static LocalDateTime toLocalDateTime(Instant instant, ZoneId zoneId) {
        return LocalDateTime.ofInstant(instant, zoneId);
    }

    /**
     * 将从1970年开始的格林毫秒数据转换为本地日期时间对象
     *
     * @param timeMills 从1970年开始的格林毫秒
     * @return 本地日期时间
     */
    public final static LocalDateTime toLocalDateTime(long timeMills, ZoneId zoneId) {
        return LocalDateTime.ofInstant(Instant.ofEpochMilli(timeMills), zoneId);
    }

    /**
     * 将jdk8本地日期时间转换为带时区的日期时间
     *
     * @param ldt  jdk8本地日期时间
     * @param zone 时区ID
     * @return 带时区的日期时间
     */
    public final static ZonedDateTime toZonedDateTime(LocalDateTime ldt, ZoneId zone) {
        return ZonedDateTime.of(ldt, zone);
    }

    /**
     * 将jdk8本地日期时间转换为毫秒
     *
     * @param ldt jdk8本地日期时间
     * @return 毫秒
     */
    public final static long toEpochMillis(LocalDateTime ldt) {
        return ldt.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
    }

    /**
     * 将jdk8本地日期转换为毫秒
     *
     * @param ld jdk8本地日期
     * @return 毫秒
     */
    public final static long toEpochMillis(LocalDate ld) {
        return ld.atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli();
    }

    /**
     * 将jdk8的日期时间对象转换为Date
     *
     * @param ldt jdk8日期
     * @return 日期时间
     */
    public final static Date toDate(LocalDateTime ldt, ZoneId zoneId) {
        return Date.from(ldt.atZone(zoneId).toInstant());
    }

    /**
     * 将jdk8的日期对象转换为Date
     *
     * @param ld jdk8日期
     * @return 日期时间
     */
    public final static Date toDate(LocalDate ld, ZoneId zoneId) {
        return Date.from(ld.atStartOfDay(zoneId).toInstant());
    }

    /**
     * JDK8 时间API获取两时间的间隔
     *
     * @param d1   本地默认时区的时间点
     * @param d2   本地默认时区的时间点
     * @param type 间隔类型
     * @return 间隔
     */
    public final static long between(LocalDateTime d1, LocalDateTime d2, Interval type) {
        if (d1.isAfter(d2)) {
            LocalDateTime temp = d1;
            d1 = d2;
            d2 = temp;
        }
        switch (type) {
            case YEAR:
                return ChronoUnit.YEARS.between(d1, d2);
            case MONTH:
                return ChronoUnit.MONTHS.between(d1, d2);
            case DAY:
                //一个时间点在0点之前另一个时间点在0点之后，则间隔为1天
                LocalDateTime _1970 = LocalDateTime.of(1970, 1, 1, 0, 0, 0, 0);
                return ChronoUnit.DAYS.between(_1970, d2) - ChronoUnit.DAYS.between(_1970, d1);
            case TIME_DAY:
                //开始时间点与结束时间点之间必须满足24小时才计为一天
                return ChronoUnit.DAYS.between(d1, d2);
            case WEEK:
                LocalDateTime __1970 = LocalDateTime.of(1970, 1, 1, 0, 0, 0, 0);
                return ChronoUnit.WEEKS.between(__1970, d2) - ChronoUnit.WEEKS.between(__1970, d1);
            case TIME_WEEK:
                return ChronoUnit.WEEKS.between(d1, d2);
            case HOUR:
                return ChronoUnit.HOURS.between(d1, d2);
            case MINUTE:
                return ChronoUnit.MINUTES.between(d1, d2);
            case SECOND:
                return ChronoUnit.SECONDS.between(d1, d2);
        }
        return ChronoUnit.MILLIS.between(d1, d2);
    }

    /**
     * JDK8 时间API获取两时间的间隔
     *
     * @param date1 时间点1
     * @param date2 时间点2
     * @param type  间隔类型
     * @return 间隔
     */
    public final static long between(Instant date1, Instant date2, Interval type, ZoneId zoneId) {
        LocalDateTime d1 = LocalDateTime.ofInstant(date1, zoneId);
        LocalDateTime d2 = LocalDateTime.ofInstant(date2, zoneId);
        return between(d1, d2, type);
    }

    /**
     * JDK8 时间API获取两时间的间隔
     *
     * @param times1 时间点1
     * @param times2 时间点2
     * @param type   间隔类型
     * @return 间隔
     */
    public final static long between(long times1, long times2, Interval type, ZoneId zoneId) {
        return between(Instant.ofEpochMilli(times1), Instant.ofEpochMilli(times2), type, zoneId);
    }

    /**
     * 判断两个时间点是否为同一天
     *
     * @param time1 时间点
     * @param time2 时间点
     * @return true为同一天
     */
    public final static boolean isSameDay(long time1, long time2) {
        return between(time1, time2, Interval.DAY, CH_ZONE) == 0;
    }

    /**
     * 获取一年总共有多少天
     *
     * @param year 年号
     * @return 天数
     */
    public static int getTotalDaysOfYear(int year) {
        int days = 365;
        if (year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)) {
            days = 366;
        }
        return days;
    }

    /**
     * 得到一年中的总周数
     *
     * @param year 年号
     * @return 周数
     */
    public static int getTotalWeeksOfYear(int year) {
        int week = 0;
        int days = getTotalDaysOfYear(year);
        //得到一年所有天数然后除以7
        week = days % 7 > 0 ? week += 1 : week;
        //得到余下几天如果有余则周+1，否则不加
        week += days / 7;
        //得到多少周
        return week;
    }

    /**
     * 获取某年的总天数
     *
     * @param year 年
     * @return 总天数
     */
    public static int countDays(int year) {
        int n = 0;
        for (int i = 1; i <= 12; i++) {
            n += countDays(i, year);
        }
        return n;
    }

    /**
     * 获取某年某月的天数
     *
     * @param year  年
     * @param month 月
     * @return 总天数
     */
    public final static int countDays(int year, int month) {
        int dom = 31;
        switch (month) {
            case 2:
                dom = (IsoChronology.INSTANCE.isLeapYear(year) ? 29 : 28);
                break;
            case 4:
            case 6:
            case 9:
            case 11:
                dom = 30;
                break;
        }
        return dom;
    }
}
